package com.jkl.service.drTable;

import java.text.ParseException;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.base.Joiner;
import com.google.common.primitives.Ints;
import com.jkl.comm.StringUtils.DataUtils;
import com.jkl.dao.drTable.DrTableMapper;
import com.jkl.dao.drTableDetail.DrTableDetailMapper;
import com.jkl.dao.drTemplate.DrTemplateMapper;
import com.jkl.dao.drTemplateDetail.DrTemplateDetailMapper;
import com.jkl.dao.person.BsPersonMapper;
import com.jkl.dao.project.PJProjectMapper;
import com.jkl.entity.drTable.DrTable;
import com.jkl.entity.drTableDetail.DrTableDetail;
import com.jkl.entity.drTemplate.DrTemplate;
import com.jkl.entity.drTemplateDetail.DrTemplateDetail;
import com.jkl.entity.person.BsPerson;
import com.jkl.entity.project.po.PJProject;

@Service
public class DrTableGenerateService {

	final static String YEAR_PATTERN = "yyyy";
	final static String MONTH_PATTERN = "yyyy-MM";
	final static String DATE_PATTERN = "yyyy-MM-dd";

	@Autowired
	DrTemplateMapper drTemplateMapper;
	@Autowired
	DrTableMapper drTableMapper;
	@Autowired
	DrTableDetailMapper drTableDetailMapper;
	@Autowired
	DrTemplateDetailMapper drTemplateDetailMapper;
	@Autowired
	BsPersonMapper bsPersonMapper;
	@Autowired
	PJProjectMapper pjProjectMapper;

	@Transactional(rollbackFor = Exception.class)
	public void generateDrTables(DrTable drTableParam) {
		if (null == drTableParam.getCheckNode() || drTableParam.getCheckNode().isEmpty()) {
			throw new TableGenerateException("请选择填报单位");
		}
		DrTemplate _template = findTemplateById(drTableParam.getTemplateId());
		DrTable generateDrTable = createInstance(drTableParam, _template);
		List<DrTemplateDetail> templateDetails = targetTemplateDetails(_template.getRowId());
		addDrTable(generateDrTable, _template, templateDetails);
	}

	/**
	 * 填报前的校验
	 */
	public void checkBeforeReport(Integer tableId) {
		List<DrTableDetail> tableDetails = drTableDetailMapper
				.queryDrTableDetail(new DrTableDetail().setTableId(tableId));
		List<String> indexCodes = tableDetails.stream().filter(item -> Ints.compare(item.getEditType(), 1) == 0)
				.map(DrTableDetail::getIndexCode).collect(Collectors.toList());
		if (CollectionUtils.isNotEmpty(indexCodes)) {
			DrTable _drTable = drTableMapper.selectByPrimaryKey(new DrTable().setRowId(tableId));
			DrTemplate _drTemplate = findTemplateById(_drTable.getTemplateId());
			checkRequiredExist(_drTable.getDeptId(), _drTable.getPeriod(), _drTemplate.getTableCycle(), indexCodes);
		}
	}

	public Map<String, String> referenceIndex(Integer tableId) {
		List<DrTableDetail> tableDetails = drTableDetailMapper
				.queryDrTableDetail(new DrTableDetail().setTableId(tableId));
		List<String> indexCodes = tableDetails.stream().filter(item -> Ints.compare(item.getEditType(), 1) == 0)
				.map(DrTableDetail::getIndexCode).collect(Collectors.toList());
		if (CollectionUtils.isNotEmpty(indexCodes)) {
			DrTable _drTable = drTableMapper.selectByPrimaryKey(new DrTable().setRowId(tableId));
			DrTemplate _drTemplate = findTemplateById(_drTable.getTemplateId());
			Stream<DrTableDetail> drTableDetailStream = referenceIndexList(_drTable.getDeptId(), _drTable.getPeriod(),
					_drTemplate.getTableCycle(), indexCodes);
			return drTableDetailStream.collect(Collectors.toMap(DrTableDetail::getIndexCode, DrTableDetail::getValue));
		}
		return Collections.emptyMap();
	}

	private void addDrTable(DrTable _table, DrTemplate _drTemplate, List<DrTemplateDetail> tplDetails) {
		System.out.println(_table);
		switch (_drTemplate.getTableLevel()) {
		case "1":
		case "2":
		case "3":
		case "4":
			addRegularTable(_table, tplDetails);
			break;
		case "5":
			addAffiliatedRangeTable(_table, _drTemplate, tplDetails);
			break;
		case "6":
			addAffiliatedSubjectTable(_table, _drTemplate, tplDetails);
			break;
		default:
			throw new TableGenerateException("未找到对应的表生成规则");
		}
	}

	/**
	 * 添加常规表，table_level 为 1，2，3，4
	 */
	private void addRegularTable(DrTable _table, List<DrTemplateDetail> templateDetails) {
		_table.getCheckNode().stream().forEach(deptNode -> {
			Integer tempDeptId = Optional.ofNullable(String.valueOf(deptNode.getRowId())).filter(StringUtils::isNotBlank)
					.map(Integer::parseInt).orElse(deptNode.getRowId());
			System.out.println(tempDeptId);
			_table.setDeptId(tempDeptId);
			findDuplicateWriteIndex(_table, templateDetails);
			addTable(_table.setDeptName(deptNode.getDeptName()), templateDetails);
		});
	}

	/**
	 * 添加归属范围表
	 */
	private void addAffiliatedRangeTable(DrTable _table, DrTemplate _drTemplate,
			List<DrTemplateDetail> templateDetails) {
		_table.getCheckNode().stream().forEach(deptNode -> {
			final Integer tempDeptId = Optional.ofNullable(deptNode.getDeptCode()).filter(StringUtils::isNotBlank)
					.map(Integer::parseInt).orElse(deptNode.getRowId());
			List<PJProject> projects = pjProjectMapper.pjProjectUnitListdeptId(tempDeptId);
			if (null == projects || projects.isEmpty()) {
				throw new TableGenerateException("没有符合生成归属范围表的区县");
			}
			String tableTplName = _table.getTemplateName();
			projects.stream().map(item -> pjProjectMapper.PJProjectByrowId(item.getPJProjectUnit().getProjectId(),
					_drTemplate.getScopeType())).filter(project -> (null != project)).forEach(project -> {
						_table.setTemplateName(Joiner.on("-").join(tableTplName, project.getProjectName()));
						_table.setDeptId(tempDeptId);
						_table.setProjectId(project.getRowId());
						addTable(_table.setDeptName(deptNode.getDeptName()), templateDetails);
					});
		});
	}

	private void addAffiliatedSubjectTable(DrTable _table, DrTemplate _drTemplate,
			List<DrTemplateDetail> templateDetails) {
		_table.getCheckNode().stream().forEach(deptNode -> {
			final Integer tempDeptId = Optional.ofNullable(deptNode.getDeptCode()).filter(StringUtils::isNotBlank)
					.map(Integer::parseInt).orElse(deptNode.getRowId());
			List<PJProject> projects = pjProjectMapper.queryProjectsByTypeAndDeptId(tempDeptId,
					_drTemplate.getScopeType());
			if (null == projects || projects.isEmpty()) {
				throw new TableGenerateException("没有符合生成归属主体表的区县");
			}
			String tableTplName = _table.getTemplateName();
			projects.stream().forEach(projectItem -> {
				_table.setTemplateName(Joiner.on("-").join(tableTplName, projectItem.getProjectName()));
				_table.setDeptId(tempDeptId);
				_table.setProjectId(projectItem.getRowId());
				addTable(_table.setDeptName(deptNode.getDeptName()), templateDetails);
			});
		});
	}

	void addTable(DrTable _drTable, List<DrTemplateDetail> tplDetails) {
//        if(CollectionUtils.isNotEmpty(drTableMapper.queryExistDrTable(_drTable))){
//            throw new TableGenerateException(StringUtils.join("上报单位：",_drTable.getDeptName(),
//                                                                "，填报周期：",_drTable.getPeriod(),"，"
//                                            ,_drTable.getTemplateName(),"已存在！"));
//        }
		_drTable.setPersonId(personByDeptId(_drTable.getDeptId()));
		drTableMapper.addDrTable(_drTable);
		addTableDetails(_drTable, tplDetails);
	}

	void addTableDetails(DrTable _drTable, List<DrTemplateDetail> tplDetails) {
		tplDetails.stream().forEach(item -> {
			DrTableDetail drTableDetail = new DrTableDetail();
			drTableDetail.setTableId(_drTable.getRowId());
			drTableDetail.setIndexCode(item.getIndexCode());
			drTableDetail.setIndexName(item.getIndexName());
			drTableDetail.setSequence(item.getSequence());
			drTableDetail.setEditType(item.getInputType());
			if (item.getDrIndex() != null) {
				drTableDetail.setUnit(item.getDrIndex().getUnit());
			}
			drTableDetail.setStatus("1");
			drTableDetailMapper.insert(drTableDetail);
		});
	}

	List<DrTemplateDetail> targetTemplateDetails(Integer templateId) {
		return drTemplateDetailMapper.selectDetailById(new DrTemplateDetail().setTableId(templateId));
	}

	private Integer personByDeptId(Integer _deptId) {
		List<BsPerson> personList = bsPersonMapper.getPersonByDept(new BsPerson().setDeptId(_deptId));
		return personList.stream().findFirst().map(BsPerson::getRowId).orElse(null);
	}

	private DrTable createInstance(DrTable drTableParam, DrTemplate drTemplate) {
		return new DrTable().setTemplateId(drTemplate.getRowId()).setTemplateName(drTableParam.getTemplateName())
				.setSequence(drTableParam.getSequence()).setOperTime(drTableParam.getOperTime())
				.setPeriod(tablePeriod(drTableParam, drTemplate.getTableCycle())).setWorkflowCode("11").setStatus("1")
				.setCheckNode(drTableParam.getCheckNode()).setTableCycle(drTemplate.getTableCycle());
	}

	private String tablePeriod(DrTable drTableParam, String templateCycle) {
		Date periodDate = tablePeriodParam(drTableParam);
		switch (templateCycle) {
		case "d":
			return formatePeriodDate(periodDate);
		case "w":
			return formatePeriodDate(DataUtils.getFirstDayOfWeek(periodDate));
		case "m":
			return DateFormatUtils.format(periodDate, MONTH_PATTERN);
		case "q":
			return formatePeriodDate(DataUtils.getFirstSeasonDate(periodDate));
		case "y":
			return DateFormatUtils.format(periodDate, YEAR_PATTERN);
		default:
			throw new TableGenerateException("模板周期不正确");
		}
	}

	private Date tablePeriodParam(DrTable drTableParam) {
		return Optional.ofNullable(drTableParam.getPeriod()).map(pe -> {
			try {
				return DateUtils.parseDate(pe, DATE_PATTERN);
			} catch (ParseException e) {
				throw new TableGenerateException("请选择正确的填报周期");
			}
		}).orElseGet(() -> new Date());
	}

	private String formatePeriodDate(Date date) {
		return DateFormatUtils.format(date, DATE_PATTERN);
	}

	private DrTemplate findTemplateById(Integer templateId) {
		DrTemplate template = drTemplateMapper.selectByPrimaryKey(new DrTemplate().setRowId(templateId));
		return Optional.ofNullable(template).orElseThrow(() -> new TableGenerateException("找不到正确的模板"));
	}

	private void findDuplicateWriteIndex(DrTable drTable, List<DrTemplateDetail> tplDetails) {
		List<String> indexcodes = tplDetails.stream()
				.filter(detailItem -> (Ints.compare(detailItem.getInputType(), 0) == 0
						&& Ints.compare(detailItem.getDrIndex().getScope(), 1) == 0))
				.map(DrTemplateDetail::getIndexCode).collect(Collectors.toList());
		if (null != indexcodes && !indexcodes.isEmpty()) {
			checkDuplicateExist(drTable, indexcodes);
		}
	}

	private void checkDuplicateExist(DrTable _drTable, List<String> indexcodes) {
		switch (_drTable.getTableCycle()) {
		case "m":
			doCheck(_drTable.getDeptId(), _drTable.getPeriod(), indexcodes);
		case "y":
			doCheck(_drTable.getDeptId(),
					StringUtils.equalsIgnoreCase("m", _drTable.getTableCycle())
							? StringUtils.substring(_drTable.getPeriod(), 0, 4)
							: _drTable.getPeriod(),
					indexcodes);
			break;
		default: // 目前不存在日报/周报/季报的情况
			throw new TableGenerateException("模板周期不正确");
		}
	}

	void doCheck(Integer deptId, String period, List<String> indexCodes) {
		List<DrTableDetail> tableDetails = drTableDetailMapper.queryReferenceIndex(deptId, period, indexCodes);
		if (CollectionUtils.isNotEmpty(tableDetails)) {
			String indexStr = tableDetails.stream().map(DrTableDetail::getIndexName).collect(Collectors.joining(","));
			throw new TableGenerateException(StringUtils.join("指标：", indexStr, "为引用指标，同一区县相同年月只能填报一次!"));
		}
	}

	void checkRequiredExist(Integer deptId, String period, String tableCycle, List<String> indexCodes) {
		List<DrTableDetail> tableDetailList = requiredTableIndexStream(deptId, period, tableCycle, indexCodes);
		if (CollectionUtils.isEmpty(tableDetailList)) {
			throw new TableIndexRequiredException(StringUtils.join("指标:", Joiner.on(",").join(indexCodes), "没有填报入口！"));
		}
		Optional<DrTableDetail> drTableDetailOptional = tableDetailList.stream()
				.filter(detailItem -> StringUtils.isBlank(detailItem.getValue())).findAny();
		if (drTableDetailOptional.isPresent()) {
			DrTableDetail drTableDetail = drTableDetailOptional.get();
			DrTable requiredTable = drTableMapper
					.selectByPrimaryKey(new DrTable().setRowId(drTableDetail.getTableId()));
			throw new TableIndexRequiredException(StringUtils.join("请先填写报表：", requiredTable.getTemplateName()));
		}
	}

	List<DrTableDetail> requiredTableIndexStream(Integer deptId, String period, String tableCycle,
			List<String> indexCodes) {
		boolean isYear = StringUtils.equalsIgnoreCase("y", tableCycle);
		List<DrTableDetail> tableDetails = drTableDetailMapper.queryReferenceIndex(deptId,
				isYear ? period : StringUtils.substring(period, 0, 4), indexCodes);
		if (!isYear) {
			List<DrTableDetail> monthsTableDetails = drTableDetailMapper.queryReferenceIndex(deptId, period,
					indexCodes);
			return Stream.of(tableDetails, monthsTableDetails).flatMap(Collection::stream).collect(Collectors.toList());
		}
		return tableDetails;
	}

	Stream<DrTableDetail> referenceIndexList(Integer deptId, String period, String tableCycle,
			List<String> indexCodes) {
		boolean isYear = StringUtils.equalsIgnoreCase("y", tableCycle);
		List<DrTableDetail> tableDetails = drTableDetailMapper.queryReferenceIndexWithValue(deptId,
				isYear ? period : StringUtils.substring(period, 0, 4), indexCodes);
		if (!isYear) {
			List<DrTableDetail> monthsTableDetails = drTableDetailMapper.queryReferenceIndexWithValue(deptId, period,
					indexCodes);
			return Stream.of(tableDetails, monthsTableDetails).flatMap(Collection::stream);
		}
		return tableDetails.stream();
	}

	public static class TableGenerateException extends RuntimeException {
		public TableGenerateException(String message) {
			super(message);
		}
	}

	public static class TableIndexRequiredException extends RuntimeException {
		public TableIndexRequiredException(String message) {
			super(message);
		}
	}
}